package cn.yj.quartz.service;

import cn.yj.quartz.QuartzJob;
import cn.yj.quartz.common.ScheduleConst;
import cn.yj.quartz.common.TaskException;
import cn.yj.quartz.job.AbstractQuartzJob;
import cn.yj.quartz.job.ConcurrentQuartzJob;
import cn.yj.quartz.job.DisConcurrentQuartzJob;
import org.apache.commons.lang3.ObjectUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @author 永健
 * @since 2022-01-14 17:07
 */
@Component
public class ScheduleExecute implements ApplicationContextAware {

    private static final Logger logger= LoggerFactory.getLogger(ScheduleExecute.class);


    private static ApplicationContext app;

    @Resource
    private Scheduler scheduler;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        app = applicationContext;
    }

    public Scheduler getScheduler(){
        return scheduler;
    }

    public static ScheduleExecute build(){
        return app.getBean(ScheduleExecute.class);
    }


    public void resumeJob(QuartzJob job) throws SchedulerException {
        getScheduler().resumeJob(createJobKey(job));
    }


    public void pauseJob(QuartzJob job) throws SchedulerException {
        getScheduler().pauseJob(createJobKey(job));
    }

    public void deleteJob(QuartzJob job) throws SchedulerException {
        getScheduler().deleteJob(createJobKey(job));
    }

    public void createJob(QuartzJob job) throws SchedulerException
    {
        if (ObjectUtils.isEmpty(job)){
            logger.info("QuartzJob is null");
            return;
        }

        Scheduler scheduler = getScheduler();

        Class<? extends AbstractQuartzJob> jobClass = job.getConcurrent()? ConcurrentQuartzJob.class :
                DisConcurrentQuartzJob.class;

        JobKey jobKey = createJobKey(job);
        JobDetail jobDetail = JobBuilder
                .newJob(jobClass)
                .withIdentity(jobKey).build();

        if (!CronExpression.isValidExpression(job.getCronExpression())){
            throw new TaskException("cron表达式错误:"+job.getCronExpression());
        }

        CronScheduleBuilder cronScheduleBuilder =
                CronScheduleBuilder
                        .cronSchedule(job.getCronExpression())
                        .withMisfireHandlingInstructionFireAndProceed();

        cronScheduleBuilder =handleCronScheduleMisfirePolicy(job,cronScheduleBuilder);

        CronTrigger trigger =
                TriggerBuilder
                        .newTrigger()
                        .withIdentity(createTriggerKey(job))
                        .withSchedule(cronScheduleBuilder).build();

        // 我们自己定义了 Job实现类，统一使用该类处理，通过反射调用目标字符串
        jobDetail.getJobDataMap().put(ScheduleConst.TASK_PROPERTIES, job);

        // 存在就删除从新执行
        if (scheduler.checkExists(jobKey)){
            scheduler.deleteJob(jobKey);
        }

        scheduler.scheduleJob(jobDetail, trigger);

        // 暂停任务
        if (job.getStatus().equals(ScheduleConst.Status.PAUSE.getValue()))
        {
            scheduler.pauseJob(jobKey);
        }
    }

    public   JobKey createJobKey(QuartzJob job){
        String jobName = job.getJobName();
        Long jobId = job.getJobId();
        return new JobKey(ScheduleConst.JOB_CLASS_NAME+jobId+jobName,job.getJobGroup());
    }

    public TriggerKey createTriggerKey(QuartzJob job){
        String jobName = job.getJobName();
        Long jobId = job.getJobId();
        return new TriggerKey(ScheduleConst.JOB_CLASS_NAME+jobId+jobName,job.getJobGroup());
    }

    /**
     * 设置定时任务策略
     */
    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(QuartzJob job, CronScheduleBuilder cb)
            throws TaskException
    {
        switch (job.getMisfirePolicy())
        {
            case ScheduleConst.MISFIRE_DEFAULT:
                return cb;
            case ScheduleConst.MISFIRE_IGNORE_MISFIRES:
                return cb.withMisfireHandlingInstructionIgnoreMisfires();
            case ScheduleConst.MISFIRE_FIRE_AND_PROCEED:
                return cb.withMisfireHandlingInstructionFireAndProceed();
            case ScheduleConst.MISFIRE_DO_NOTHING:
                return cb.withMisfireHandlingInstructionDoNothing();
            default:
                throw new TaskException("执行策略有误");
        }
    }


}
